// Original program code by Heather Dewey-Hagborg, http://www.arduino.cc/en/Tutorial/SPIEEPROM
// with slight modifications to work with 25cl0l by Chad Phillips
#include <Wire.h>
#include <string.h>
#include <stdio.h>

uint8_t outbuf[6];		// array to store arduino output
int ledPin = 8;

int startRecording = 1;

#define DATAOUT 11		//MOSI
#define DATAIN  12		//MISO
#define SPICLOCK  13		//sck
#define SLAVESELECT 10		//ss

//opcodes
#define WREN  6
#define WRDI  4
#define RDSR  5
#define WRSR  1
#define READ  3
#define WRITE 2

/*
Min, Max, Middle, range
X: 74, 177, 125.5, 103
Y: 72, 175, 123.5, 103
Z: 79, 187, 133  , 108
*/

byte clr;
int gCnt = 0;

char
spi_transfer (volatile char data)
{
  SPDR = data;			// Start the transmission
  while (!(SPSR & (1 << SPIF)))	// Wait the end of the transmission
    {
    };
  return SPDR;			// return the received byte
}

void
setup ()
{
  Serial.begin (19200);
  Serial.println ("Setup start");

  pinMode (DATAOUT, OUTPUT);
  pinMode (DATAIN, INPUT);
  pinMode (SPICLOCK, OUTPUT);
  pinMode (SLAVESELECT, OUTPUT);
  digitalWrite (SLAVESELECT, HIGH);	//disable device

  // SPCR = 01010000
  //interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
  //sample on leading edge of clk,system clock/4 rate (fastest)
  SPCR = (1 << SPE) | (1 << MSTR);
  clr = SPSR;
  clr = SPDR;
  Serial.println ("Setup start 1");
  /*
     int a = 0;
     while ( a < 1000) {
     write_byte (a , a);
     a++;
     }
   */
  Serial.println ("Setup start 2");
  Wire.begin ();		// join i2c bus with address 0x52
  nunchuck_init ();		// send the initilization handshake
  Serial.println ("Setup finshed");
}

void
start_eeprom_transfer (char xData, char yData, char zData, int gCnt)
{
  // Write to first two bytes of eeprom
  // what the count of acceleration readings is
  write_byte ((char) (gCnt >> 8), 0);
  write_byte (gCnt, 1);

  // Write acceleration data to eeprom
  write_byte (xData, gCnt * 3 + 2);
  write_byte (yData, gCnt * 3 + 3);
  write_byte (zData, gCnt * 3 + 4);
}

void
write_byte (char data, int address)
{
  digitalWrite (SLAVESELECT, LOW);
  spi_transfer (WREN);		//write enable
  digitalWrite (SLAVESELECT, HIGH);
  delay (10);
  digitalWrite (SLAVESELECT, LOW);
  spi_transfer (WRITE);		//write instruction
  spi_transfer ((char) (address >> 8));	//send MSByte address first
  spi_transfer ((char) (address));	//send LSByte address

  // start spi transfer
  spi_transfer (data);		//write data byte
  digitalWrite (SLAVESELECT, HIGH);	//release chip
  delay (20);
}

byte
read_eeprom (int EEPROM_address)
{
  //READ EEPROM
  int data;
  digitalWrite (SLAVESELECT, LOW);
  spi_transfer (READ);		//transmit read opcode
  spi_transfer ((char) (EEPROM_address >> 8));	//send MSByte address first
  spi_transfer ((char) (EEPROM_address));	//send LSByte address
  data = spi_transfer (0xFF);	//get data byte
  digitalWrite (SLAVESELECT, HIGH);	//release chip, signal end transfer
  return data;
}

void
loop ()
{
  int cnt = 0;
  Wire.requestFrom (0x52, 6);	// request data from nunchuck
  while (Wire.available ())
    {
      outbuf[cnt] = nunchuk_decode_byte (Wire.receive ());	// receive byte as an integer
      cnt++;
    }

  // If we recieved the 6 bytes, then go print them
  if (cnt >= 5)
    {
      process_nunchuck_data ();
    }

  cnt = 0;
  send_zero ();			// send the request for next bytes
  delay (10);
}

void
unload_data ()
{
  Serial.println ("Staring Data dump");
  delay (1000);
  int address = 0;
  byte eeprom_output_data;

  int aCnt = read_eeprom (address);
  address++;
  int bCnt = read_eeprom (address);
  address++;
  Serial.print ("Count");
  Serial.println (gCnt, DEC);
  Serial.print ("Top bits: ");
  Serial.print (aCnt, DEC);
  Serial.print (" bottom bits: ");
  Serial.println (bCnt, DEC);

  delay (3000);
  while (address < (gCnt * 3 + 2))
    {
      int xData = read_eeprom (address);
      address++;
      int yData = read_eeprom (address);
      address++;
      int zData = read_eeprom (address);
      address++;

      Serial.print (xData, DEC);
      Serial.print ("\t");
      Serial.print (yData, DEC);
      Serial.print ("\t");
      Serial.print (zData, DEC);
      Serial.println ();

      delay (20);
    }
  Serial.println ("Data dump finished.");
  delay (3000);
}

void
nunchuck_init ()
{
  Serial.println ("Nunchuck init start.");
  Wire.beginTransmission (0x52);	// transmit to device 0x52
  Wire.send (0x40);		// sends memory address
  Wire.send (0x00);		// sends sent a zero.  
  Wire.endTransmission ();	// stop transmitting
  Serial.println ("Nunchuck init end.");
}

void
send_zero ()
{
  Wire.beginTransmission (0x52);	// transmit to device 0x52
  Wire.send (0x00);		// sends one byte
  Wire.endTransmission ();	// stop transmitting
}

// Print the input data we have recieved
// accel data is 10 bits long
// so we read 8 bits, then we have to add
// on the last 2 bits.  That is why I
// multiply them by 2 * 2
void
process_nunchuck_data ()
{
  // byte outbuf[5] contains bits for z and c buttons
  // it also contains the least significant bits for the accelerometer data
  // so we have to check each bit of byte outbuf[5]
  int joy_x_axis = outbuf[0];
  int joy_y_axis = outbuf[1];

  int accel_x_axis = (outbuf[2] << 2) + ((outbuf[5] >> 2) & 0x03);
  int accel_y_axis = (outbuf[3] << 2) + ((outbuf[5] >> 4) & 0x03);
  int accel_z_axis = (outbuf[4] << 2) + ((outbuf[5] >> 6) & 0x03);

  int z_button = outbuf[5] & 1;
  int c_button = outbuf[5] & 2;

  if (startRecording < 1)
    {
      digitalWrite (ledPin, HIGH);	// sets the LED on
      start_eeprom_transfer (accel_x_axis / 4, accel_y_axis / 4,
			     accel_z_axis / 4, gCnt);
      gCnt++;
    }
  else
    {
      digitalWrite (ledPin, LOW);	// sets the LED off
    }

  if (c_button < 1)
    {
      unload_data ();
      gCnt = 0;
      startRecording = 1;
    }

  if (z_button < 1)
    {
      startRecording = 0;
    }

  Serial.print (joy_x_axis / 4, DEC);
  Serial.print ("\t");

  Serial.print (joy_y_axis / 4, DEC);
  Serial.print ("\t");

  Serial.print (accel_x_axis / 4, DEC);
  Serial.print ("\t");

  Serial.print (accel_y_axis / 4, DEC);
  Serial.print ("\t");

  Serial.print (accel_z_axis / 4, DEC);
  Serial.print ("\t");

  Serial.print (z_button, DEC);
  Serial.print ("\t");

  Serial.print (c_button, DEC);
  Serial.print ("\t");
  Serial.print ("\r\n");
}

// Encode data to format that most wiimote drivers except
// only needed if you use one of the regular wiimote drivers
char
nunchuk_decode_byte (char x)
{
  x = (x ^ 0x17) + 0x17;
  return x;
}
